home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / os2 / srefv112.zip / SREFPRC1.ZIP / MAPIMAGE.SRF < prev    next >
Text File  |  1996-05-10  |  16KB  |  426 lines

  1. /* ------------------- Mappable images processor -----------------------------*/
  2. /*  SREFILTR's  "mappable" images processor.
  3. .
  4. .   The bulk of this (the READMAP, GetURLfromMAP, and CrossingsMultiplyTest)
  5. .   was taken pretty much verbatim from the GOHTTP package:
  6. .       "GoHTTP REXX Filter Script for GoServe v2.00+ for OS/2
  7. .        by Donald L. Meyer
  8. .
  9. .    A NSCA style MAP file is expected, which should contain
  10. .    instructions as to what regions of the image map to
  11. .    what urls.  Four types of regions are recognized:
  12. .       circles, rectangles, polygons, points.
  13.     If the selected point falls within a circle, rectangle, or polygon,
  14. .    or it's exactly on a point, then we have a direct match.  If this doesn't
  15. .    occur, and there are points selected, then it is assigned to the closest point,
  16. .    given that the distance to this closest point is less then max_pointdist (in pixel)
  17. .    If none of these satisfied, then use default_url.
  18. */
  19. /* -------------------------------------------------------------*/
  20.  
  21. /* ----------------------------------------------------------------------- */
  22. /*  Main routine for processing mappable images respones */
  23. /* ----------------------------------------------------------------------- */
  24.  
  25. sref_mapimage:
  26.  
  27. parse arg  mapfile,awords, servername, serverport, tempfile, dir, max_pointdist
  28.  
  29.       awords=packur(awords)
  30.  
  31.       default_url=""
  32.  
  33.       parse var awords ax ',' ay  .
  34.       parse var ay ay '?' .             /* get rid of accidentally added junk*/
  35.  
  36.       if datatype(ax)<>"NUM" | datatype(ay)<>"NUM" then signal noxy
  37.       if datatype(max_pointdist)<>"NUM" then max_pointdist=50
  38.  
  39. /* check for mapfile, or mapfile.map */
  40.       aa=stream(mapfile,'c','query exists')
  41.       if aa="" & pos('.',mapfile)=0 then do   /* add .map if non existent map file and no . */
  42.             mapfile=mapfile'.MAP'
  43.        end
  44.       ause=fileread(mapfile,'filelines',,'E')
  45.       doit=filelines.0
  46.  
  47.       if doit=0 then signal nomap      /* no such map file */
  48.  
  49.       region.0=0
  50.       nr2=readmap()   /* sets default_url and Region,
  51.                          expects filelines. servername port */
  52.  
  53.       if nr2=0 & default_url="" then signal nourl
  54.           say " Using mapfile: " mapfile " , # regions=" region.nregions
  55.  
  56.       message=geturlfrommap(ax, ay)
  57.  
  58.       if message="" then signal nomatch   /* could not find a url */
  59.  
  60.       message=sref_fix_url(message,servername,serverport)
  61.  
  62. /* we send back to the server a "redirect to this found url" response */
  63.       say " Moved to Url: " message
  64.  
  65.  /* Send back resonse headers */
  66.      'RESPONSE HTTP/1.0 302 Moved Temporarily'    /* Set HTTP response line */
  67.      'HEADER ADD URI: 'message
  68.      'HEADER ADD Location: 'message
  69.      doc = '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
  70.  
  71.       'VAR TYPE text/html BINARY NAME doc'
  72.       return 1
  73.      say " Redirect to " message
  74.  
  75.  
  76.   /* error returns ... */
  77. nourl:                  /* jump here if no such url found */
  78.    call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
  79.  
  80.   call lineout tempfile, "<html><head><title> No such matching URL </title>"
  81.   call lineout tempfile, "</head>"
  82.   call lineout tempfile, "<body><h2>Could not find any URLS.</h2>"
  83.   call lineout tempfile, ' No URLS were listed in the "map" file ' mapfile0
  84.   call lineout tempfile, ' </body> </html> '
  85.   say " Empty mapfile: " mapfile
  86.   'FILE ERASE TYPE text/html NAME '||tempfile
  87.    return 0
  88.  
  89.  
  90. nomatch:                  /* jump here if no url found */
  91.    call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
  92.  
  93.   call lineout tempfile, "<html><head><title> No  URL selected </title>"
  94.   call lineout tempfile, "</head>"
  95.   call lineout tempfile, "<body><h2>A URL was not selected</h2>"
  96.   call lineout tempfile, ' You selected a region NOT associated with a URL: ' ax ay
  97.   call lineout tempfile, ' </body> </html> '
  98.   say ' No URL match: ' ax ay mapfile
  99.   'FILE ERASE TYPE text/html NAME 'tempfile
  100.   return 0
  101.  
  102. nomap:
  103.    call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
  104.  
  105.   call lineout tempfile, "<html><head><title> No such MAP file </title>"
  106.   call lineout tempfile, "</head>"
  107.   call lineout tempfile, "<body><h2>Could not find MAP file.</h2>"
  108.   call lineout tempfile, ' The "map" file ' mapfile0 ' could not be found.'
  109.   call lineout tempfile, ' </body> </html> '
  110.   say ' No such mapfile: ' mapfile
  111.   'FILE ERASE TYPE text/html NAME 'tempfile
  112.   return 0
  113.  
  114. noxy:                           /* invalid x y */
  115.    call lineout tempfile, '<!doctype html public "-//IETF//DTD HTML 2.0//EN">'
  116.  
  117.   call lineout tempfile, "<html><head><title> Bad location </title>"
  118.   call lineout tempfile, "</head>"
  119.   call lineout tempfile, "<body><h2>Invalid pixel location given.</h2>"
  120.   call lineout tempfile, ' The location given is invalid:' ax " , " ay
  121.   call lineout tempfile, ' </body> </html> '
  122.   say ' Bad x y: ' ax ay
  123.   'FILE ERASE TYPE text/html NAME 'tempfile
  124.   return 0
  125.  
  126.  
  127.  
  128.  
  129.  
  130. /* ----------------------------------------------------------------------- */
  131. /* READMAP: Read in the .MAP file into a stem variable.
  132. .    The stem variable Region. gets filled up with "region" info.
  133. .    Also, the default_url gets set (if a DEFAULT line is found)           */
  134. /* ----------------------------------------------------------------------- */
  135.  
  136. readMap: procedure expose Region. Default_URL filelines.  ServerName Port
  137.  
  138.  
  139.         /* Initilizations */
  140.    i = 0
  141.    nR = 0
  142.    strongchecks=1               /* always check for proper syntax */
  143.    Text = '%'
  144.    Default_URL = ''
  145.  
  146. /* read file into filelines. array */
  147.  
  148.  
  149.         /* read in the region definitions from the .MAP file.  */
  150.    do jj=1 to filelines.0
  151.      text=strip(filelines.jj)
  152.      if text="" then iterate            /* ignore blank lines */
  153.      if  left(Text,1)= '#' then iterate  /* # starts a comment line */
  154.  
  155.      i = i + 1
  156.  
  157.      parse var Text Text '#' comments   /* trim any comments   */
  158.      r = right(Text,1)
  159.      l = left(comments,1)
  160.      if (((r \= ' ') & (r \= '') & (r \= '09'x)) & ((l \= ' ') & (l \= '') & (l \= '09'x))) then do
  161.         parse var comments comments'#'rest      /* trim any comments, again   */
  162.         Text = Text'#'comments
  163.      end
  164.  
  165.      parse var Text T  Region.URL.i  Cs
  166.      parse var Cs C1 C2
  167.      parse upper var T Region.Type.i
  168.  
  169.      Err = 0
  170.      Select
  171.  
  172.  
  173.         /* DEFAULT keyword sets the default URL to redirect to in case of no region matches. */
  174.        When (Region.Type.i = 'DEFAULT') then do
  175.            Default_URL = Region.URL.i
  176.            i = i - 1
  177.          end
  178.  
  179.         /* Parse out coordinates for the Rectangular region.  */
  180.        When (Region.Type.i = 'RECT') then do
  181.            parse var C1 Region.X1.i ',' Region.Y1.i
  182.            parse var C2 Region.X2.i ',' Region.Y2.i
  183.            if (StrongChecks) then do
  184.               if ((Datatype(Region.X1.i) \= 'NUM') | (Datatype(Region.Y1.i) \= 'NUM') | (Datatype(Region.X2.i) \= 'NUM') | (Datatype(Region.Y2.i) \= 'NUM')) then Err = 1
  185.            end
  186.  
  187.            if (Err == 0) then do
  188.               nR = nR + 1
  189.         /* ensure that X1,Y1 is upper left, and X2,Y2 is lower right... */
  190.               if (Region.X2.i < Region.X1.i) then       /* Swap... */
  191.                do 1
  192.                  a = Region.X2.i
  193.                  Region.X2.i = Region.X1.i
  194.                  Region.X1.i = a
  195.                end
  196.               if (Region.Y2.i < Region.Y1.i) then       /* Swap... */
  197.                do
  198.                  a = Region.Y2.i
  199.                  Region.Y2.i = Region.Y1.i
  200.                  Region.Y1.i = a
  201.                end
  202.             end
  203.          end
  204.  
  205.         /* Parse out coordinates for the Circle region.  */
  206.        When (Region.Type.i = 'CIRC') then do
  207.            parse var C1 Region.X1.i ',' Region.Y1.i
  208.                 /* radius... */
  209.            Region.X2.i = C2
  210.            if (StrongChecks) then do
  211.               if ((Datatype(Region.X1.i) \= 'NUM') | (Datatype(Region.Y1.i) \= 'NUM') | (Datatype(Region.X2.i) \= 'NUM')) then Err = 1
  212.            end
  213.            if (Err == 0) then do
  214.               Region.radius2.i = (Region.X2.i**2)
  215.               nR = nR + 1
  216.            end
  217.          end
  218.  
  219.         /* Parse out coordinates for the Circle region.  */
  220.        When (Region.Type.i = 'CIRCLE') then do
  221.            parse var C1 Region.X1.i ',' Region.Y1.i
  222.            parse var C2 Region.X2.i ',' Region.Y2.i
  223.  
  224.            if (StrongChecks) then do
  225.               if ((Datatype(Region.X1.i) \= 'NUM') | (Datatype(Region.Y1.i) \= 'NUM') | (Datatype(Region.X2.i) \= 'NUM') | (Datatype(Region.Y2.i) \= 'NUM')) then Err = 1
  226.            end
  227.  
  228.            if (Err == 0) then do
  229.                 /* radius... */
  230.               qX = (Region.X1.i - Region.X2.i)
  231.               qY = (Region.Y1.i - Region.Y2.i)
  232.               Region.radius2.i = (qX*qX) + (qY*qY)
  233.               nR = nR + 1
  234.            end
  235.          end
  236.  
  237.         /* handle the Poly region.  */
  238.        When (Region.Type.i = 'POLY') then do
  239.            k=1
  240.            do while (strip(Cs) \= '') & (Err == 0)
  241.               parse var Cs Region.X.i.k ',' Region.Y.i.k Cs
  242.               if (StrongChecks) then if ((Datatype(Region.X.i.k) \= 'NUM') | (Datatype(Region.Y.i.k) \= 'NUM')) then Err = 1
  243.               k = k + 1
  244.            end
  245.  
  246.            if (Err == 0) then do
  247.               Region.NVerts.i = (k - 1)
  248.               Region.X.i.k = -1
  249.               nR = nR + 1
  250.            end
  251.  
  252.          end
  253.  
  254.         /* handle the Point region.  */
  255.        When (Region.Type.i = 'POINT') then do
  256.            parse var C1 Region.X1.i ',' Region.Y1.i
  257.            if (StrongChecks) then do
  258.               if ((Datatype(Region.X1.i) \= 'NUM') | (Datatype(Region.Y1.i) \= 'NUM')) then Err = 1
  259.            end
  260.            if (Err == 0) then nR = nR + 1
  261.          end
  262.  
  263.         /* handle the standard blank line between 'Default' line and other regions... */
  264.        When (Region.Type.i = '') then do
  265.            if ((i = 1) & (Default_URL \= '')) then do
  266.               Text = '%'
  267.               i = i - 1
  268.             end
  269.          end
  270.  
  271.         /* Must be an unknown region type... */
  272.        Otherwise do
  273.            if (Text \= '') then
  274.               say 'Unknown RegionType=['Region.Type.i']  URL=<'Region.URL.i'>  [C1='C1'  C2='C2']'
  275.            i = i - 1
  276.          end
  277.      end /* Select */
  278.      if (Err == 1) then say 'error:  region #'i
  279.      if (Err == 1) then i = i - 1
  280.    end
  281.    rc = stream(Map.filename.ID, 'C', 'CLOSE')
  282.    Region.NRegions = nR
  283.    return nR
  284.  
  285.  
  286. /* ----------------------------------------------------------------------- */
  287. /* GetURLfromMap: Identify the region of the map, & return the associated URL */
  288. /* ----------------------------------------------------------------------- */
  289.  
  290. GetURLfromMap: procedure expose Region. Default_URL max_pointdist
  291.  
  292.         /* Parse out mouse click coordinates */
  293.    parse arg tX , tY
  294.  
  295.    i = 1;   Hit = 0;  sawpoint = 0
  296.  
  297.         /* Set URL to the default, in case no regions are hit... */
  298.    _URL = Default_URL
  299.  
  300.         /* if tX & tY = '', then assume web client not imagemap capable - bypass region search. */
  301.    if (tX='') then Hit = -1
  302.  
  303.  
  304.         /* Loop through the defined regions to find first hit. */
  305.    do while ((i <= Region.NRegions) & (Hit = 0))
  306.      Select
  307.  
  308.         /* Determine if coordinates lie within the rectangular area.  */
  309.        When Region.Type.i = 'RECT' then do
  310.           Hit = ((tX >= Region.X1.i) & (tY >= Region.Y1.i) & (tX <= Region.X2.i) & (tY <= Region.Y2.i))
  311.         end
  312.  
  313.         /* Calc distance to coordinates from Circle center, and compare to radius.*/
  314.         /*   If less than radius, then it's a hit... */
  315.        When (Region.Type.i = 'CIRC') | (Region.Type.i = 'CIRCLE') then do
  316.           a = tX - Region.X1.i
  317.           b = tY - Region.Y1.i
  318.           R = a**2 + b**2
  319.           Hit = (R <= Region.radius2.i)
  320.         end
  321.  
  322.         /* Determine if coordinates lie within the polygon.  */
  323.        When Region.Type.i = 'POLY' then do
  324.            Hit = CrossingsMultiplyTest(i, tX, tY)
  325.         end
  326.  
  327.        When Region.Type.i = 'POINT' then do
  328.           a = tX - Region.X1.i
  329.           b = tY - Region.Y1.i
  330.           R = (a * a) + (b * b)
  331.         /* If a direct hit, then don't bother with nearest determinations... */
  332.           if (R == 0) then Hit = 1
  333.         /* otherwise, track to find which point is nearest the click coordinates... */
  334.           else if (sawpoint) then do
  335.              if (R < PointDistance) then do
  336.                 PointDistance = R
  337.                 ClosestPoint = i
  338.              end
  339.           end
  340.           else do
  341.              sawpoint = 1
  342.              PointDistance = R
  343.              ClosestPoint = i
  344.           end
  345.         end
  346.  
  347.  
  348.         /* The required 'Otherwise'... */
  349.        Otherwise  do
  350.         end
  351.      End /* Select */
  352.  
  353.         /* If a hit, then set '_URL' to stem URL value.  */
  354.      if (Hit = 1) then do
  355.            _URL = Region.URL.i
  356.      end
  357.      i = i + 1
  358.    end
  359.  
  360.    if (Hit == 0) & (sawpoint) & (pointdistance < (max_pointdist*max_pointdist)) then do
  361.       _URL = Region.URL.ClosestPoint
  362.    end
  363.  
  364.    return _URL          /* return the identified URL */
  365.  
  366. /* ----------------------------------------------------------------------- */
  367. /* ======= Crossings Multiply algorithm ===================================
  368. .    point in polygon inside/outside code.
  369. .  Original C code by Eric Haines, 3D/Eye Inc, erich@eye.com
  370. .  based on work by Joseph Samosky and Mark Haigh-Hutchinson.
  371. .  Ported to REXX for this filter by D.L. Meyer, meyer@larch.ag.uiuc.edu
  372. */
  373. /* ----------------------------------------------------------------------- */
  374.  
  375. CrossingsMultiplyTest: Procedure expose Region.
  376.     parse arg pgon, pointX, pointY
  377.  
  378.     numverts = Region.NVerts.pgon
  379.     vtx0X = Region.X.pgon.numverts
  380.     vtx0Y = Region.Y.pgon.numverts
  381.     /* get test bit for above/below X axis */
  382.     yflag0 = ( vtx0Y >= pointY )
  383.  
  384.     inside_flag = 0
  385.     do j = 1 to numverts
  386.               vtx1X = Region.X.pgon.j
  387.               vtx1Y = Region.Y.pgon.j
  388.  
  389.         yflag1 = ( vtx1Y >= pointY )
  390.         /* Check if endpoints straddle (are on opposite sides) of X axis
  391.          * (i.e. the Y's differ); if so, +X ray could intersect this edge.
  392.          * The old test also checked whether the endpoints are both to the
  393.          * right or to the left of the test point.  However, given the faster
  394.          * intersection point computation used below, this test was found to
  395.          * be a break-even proposition for most polygons and a loser for
  396.          * triangles (where 50% or more of the edges which survive this test
  397.          * will cross quadrants and so have to have the X intersection computed
  398.          * anyway).  I credit Joseph Samosky with inspiring me to try dropping
  399.          * the "both left or both right" part of my code.
  400.          */
  401.         if ( yflag0 \= yflag1 ) then do
  402.             /* Check intersection of pgon segment with +X ray.
  403.              * Note if >= point's X; if so, the ray hits it.
  404.              * The division operation is avoided for the ">=" test by checking
  405.              * the sign of the first vertex wrto the test point; idea inspired
  406.              * by Joseph Samosky's and Mark Haigh-Hutchinson's different
  407.              * polygon inclusion tests.
  408.              */
  409.             if ( (((vtx1Y-pointY) * (vtx0X-vtx1X)) >= ((vtx1X-pointX) * (vtx0Y-vtx1Y))) == yflag1 ) then do
  410.                 inside_flag = (inside_flag  == 0)
  411.             end
  412.         end
  413.  
  414.         /* Move to the next pair of vertices, retaining info as possible. */
  415.         yflag0 = yflag1
  416.         vtx0X = vtx1X
  417.         vtx0Y = vtx1Y
  418.  
  419.     end
  420.  
  421.     return  inside_flag
  422.  
  423.  
  424.  
  425.  
  426.